
/*----------------------------------------------------------------------------
 * Copyright (c) <2013-2015>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/
#include "stdio.h"
#include "stdlib.h"
#include "los_atomic.h"
#include "hisoc/usb3.h"
#include "asm/hal_platform_ints.h"
#include "asm/delay.h"

/* offset 0x140 */
#define USB2_CTRL   IO_ADDRESS(CRG_REG_BASE + 0x140)
#define USB2_CRG_DEFAULT_VAL    0x3b2f
#define USB2_UTMI_CKEN          (0x1<<12)
#define USB2_PHY_APB_CKEN       (0x1<<11)
#define USB2_REF_CKEN           (0x1<<9)
#define USB2_BUS_CKEN           (0x1<<8)
#define USB2_PHY_PLL_CKEN       (0x1<<4)
#define USB2_PHY_XTAL_CKEN      (0x1<<2)
#define USB2_FREECLK_CKSEL      (0x1<<13)
#define USB2_PHY_APB_RST        (0x1<<10)
#define USB2_VCC_SRST_REQ       (0x1<<3)
#define USB2_PHY_REQ            (0x1<<0)
#define USB2_PHY_PORT_TREQ      (0x1<<1)

#define CTRL_BASE_REG           0x10030000
#define GTXTHRCFG                   IO_ADDRESS(CTRL_BASE_REG + 0xc108)
#define GRXTHRCFG                   IO_ADDRESS(CTRL_BASE_REG + 0xc10c)
#define REG_GCTL                      IO_ADDRESS(CTRL_BASE_REG + 0xc110)
#define REG_GUSB3PIPECTL0     IO_ADDRESS(CTRL_BASE_REG + 0xc2c0)
#define PCS_SSP_SOFT_RESET   (0x1u << 31)

#define PORT_CAP_DIR                    (0x3 << 12)
#define PORT_SET_HOST                   (0x1 << 12)
#define PORT_DISABLE_SUSPEND    (0x1 << 17)

#define USB2_G_TXTHRCFG                 0x23100000
#define USB2_G_RXTHRCFG                 0x23100000

/* PHY base register*/
#define USB2_PHY_BASE_REG       0x100d0000
#define RG_PLL_EN_MASK          0x0003
#define RG_PLL_EN_VAL           0x0003

#define USB2_VBUS_IO_BASE_REG   0x100c0000
#define USB2_VBUS_IO_OFFSET     0x7c
#define USB_VBUS_IO_CONFIG_VAL  0x0531

#define USB_PWREN_CONFIG_REG    0x100c0080
#define USB_PWREN_CONFIG_VAL    0x1

/* PHY eye config */
#define HIXVP_PHY_ANA_CFG_0_OFFSET                      0x00
#define HIXVP_PHY_PRE_DRIVE_MASK                        (0xf << 24)
#define HIXVP_PHY_PRE_DRIVE_VAL                         0x4 << 24
#define HIXVP_PHY_ANA_CFG_2_OFFSET                      0x08
#define HIXVP_PHY_TX_TEST_BIT                           0x1 << 20
#define HIXVP_PHY_DISCONNECT_REFERENCE_MASK     (0x7 << 16)
#define HIXVP_PHY_DISCONNECT_REFERENCE_VAL      0x2 <<16
#define HIXVP_PHY_ANA_CFG_4_OFFSET                      0x10
#define HIXVP_PHY_TX_REFERENCE_MASK                     (0x7 << 4)
#define HIXVP_PHY_TX_REFERENCE_VAL                      0x5 << 4

/* PHY trim config */
#define USB_TRIM_BASE_REG                                       0x12028004
#define USB_TRIM_VAL_MASK                                       0x001F
#define USB_TRIM_VAL_MIN                                        0x0009
#define USB_TRIM_VAL_MAX                                        0x001D
#define USB2_TRIM_OFFSET                                        0x0008
#define USB2_TRIM_MASK                                          0x1f00
#define USB2_TRIM_VAL(a)                                        (((a) << 8) & USB2_TRIM_MASK)
#define USB2_TRIM_DEFAULT_VAL                           0x000e

/* PHY svb config */
#define USB_SVB_BASE_REG                                        0x12020158
#define USB_SVB_OFFSET                                          0x00
#define USB_SVB_MASK                                            (0x0f << 24)
#define USB_SVB_PREDEV_5_MIN                            0x2bc
#define USB_SVB_PREDEV_5_MAX_4_MIN                      0x32a
#define USB_SVB_PREDEV_4_MAX_3_MIN                      0x398
#define USB_SVB_PREDEV_3_MAX_2_MIN                      0x3ca
#define USB_SVB_PREDEV_2_MAX                            0x44c
#define USB_SVB_PREDEV_5_PHY_VAL                        0x05 << 24
#define USB_SVB_PREDEV_4_PHY_VAL                        0x04 << 24
#define USB_SVB_PREDEV_3_PHY_VAL                        0x03 << 24
#define USB_SVB_PREDEV_2_PHY_VAL                        0x02 << 24

static int otg_usbdev_stat = 0;

static void usb_eye_config(void)
{
    unsigned int reg;
    /* HSTX pre-drive strength */
    reg = GET_UINT32(USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_0_OFFSET);
    reg &= ~HIXVP_PHY_PRE_DRIVE_MASK;
    reg |= HIXVP_PHY_PRE_DRIVE_VAL;
    WRITE_UINT32(reg, USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_0_OFFSET);

    /* TX test bit */
    reg = GET_UINT32(USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_2_OFFSET);
    reg |= HIXVP_PHY_TX_TEST_BIT;
    WRITE_UINT32(reg, USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_2_OFFSET);

    /* Disconnect reference voltage sel */
    reg = GET_UINT32(USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_2_OFFSET);
    reg &= ~HIXVP_PHY_DISCONNECT_REFERENCE_MASK;
    reg |= HIXVP_PHY_DISCONNECT_REFERENCE_VAL;
    WRITE_UINT32(reg, USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_2_OFFSET);

    /* TX reference voltage sel */
    reg = GET_UINT32(USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_4_OFFSET);
    reg &= ~HIXVP_PHY_TX_REFERENCE_MASK;
    reg |= HIXVP_PHY_TX_REFERENCE_VAL;
    WRITE_UINT32(reg,USB2_PHY_BASE_REG + HIXVP_PHY_ANA_CFG_4_OFFSET);
}

void usb_trim_config(void)
{
    unsigned int ret;
    unsigned int reg;
    unsigned int trim_val;
    ret = GET_UINT32(USB_TRIM_BASE_REG);
    trim_val = (ret & USB_TRIM_VAL_MASK); // get usb trim value
    reg = GET_UINT32(USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
    reg &= ~USB2_TRIM_MASK;
    /*set trim value to HiXVPV100 phy*/
    if ((trim_val >= USB_TRIM_VAL_MIN) && (trim_val <= USB_TRIM_VAL_MAX)) {
        reg |= USB2_TRIM_VAL(trim_val);
    }
    else {
        reg |= USB2_TRIM_VAL(USB2_TRIM_DEFAULT_VAL);
    }

    WRITE_UINT32(reg, USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
}

void usb_svb_config(void)
{
    unsigned int ret;
    unsigned int reg;
    ret = GET_UINT32(USB_SVB_BASE_REG);
    reg = GET_UINT32(USB2_PHY_BASE_REG + USB_SVB_OFFSET);
    reg &= ~USB_SVB_MASK;
    /*set svb value to HiXVPV100 phy*/
    if ((ret >= USB_SVB_PREDEV_5_MIN) && (ret < USB_SVB_PREDEV_5_MAX_4_MIN))
        reg |= USB_SVB_PREDEV_5_PHY_VAL;
    else if ((ret >= USB_SVB_PREDEV_5_MAX_4_MIN) && (ret < USB_SVB_PREDEV_4_MAX_3_MIN))
        reg |= USB_SVB_PREDEV_4_PHY_VAL;
    else if ((ret >= USB_SVB_PREDEV_4_MAX_3_MIN) && (ret <= USB_SVB_PREDEV_3_MAX_2_MIN))
        reg |= USB_SVB_PREDEV_3_PHY_VAL;
    else if ((ret > USB_SVB_PREDEV_3_MAX_2_MIN) && (ret <= USB_SVB_PREDEV_2_MAX))
        reg |= USB_SVB_PREDEV_2_PHY_VAL;
    else
        reg |= USB_SVB_PREDEV_4_PHY_VAL;

    WRITE_UINT32(reg, USB2_PHY_BASE_REG + USB_SVB_OFFSET);
}

static void usb_vbus_config(void)
{
    WRITE_UINT32(USB_VBUS_IO_CONFIG_VAL, USB2_VBUS_IO_BASE_REG + USB2_VBUS_IO_OFFSET);
    udelay(20);
}

static void usb_crg_c(void)
{
    unsigned int reg, reg_1;

    reg = USB_PWREN_CONFIG_VAL;
    WRITE_UINT32(reg, USB_PWREN_CONFIG_REG);

    /*set usb2 CRG default val*/
    reg = USB2_CRG_DEFAULT_VAL;
    WRITE_UINT32(reg, USB2_CTRL);
    udelay(200);

    /*open UTMI clk*/
    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_UTMI_CKEN;
    WRITE_UINT32(reg, USB2_CTRL);

    /*open phy apb clk*/
    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_PHY_APB_CKEN;
    WRITE_UINT32(reg, USB2_CTRL);

    /*open ctrl ref clk*/
    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_REF_CKEN;
    WRITE_UINT32(reg, USB2_CTRL);

    /*open bus clk*/

    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_BUS_CKEN;
    WRITE_UINT32(reg, USB2_CTRL);

    /*open phy pll clk*/
    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_PHY_PLL_CKEN;
    WRITE_UINT32(reg, USB2_CTRL);

    /*open phy xtal clk*/
    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_PHY_XTAL_CKEN;
    WRITE_UINT32(reg, USB2_CTRL);

    /*freeclk_cksel_free*/
    reg = GET_UINT32(USB2_CTRL);
    reg |= USB2_FREECLK_CKSEL;
    WRITE_UINT32(reg, USB2_CTRL);

    udelay(100);

    /*release phy apb*/
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_PHY_APB_RST;
    WRITE_UINT32(reg, USB2_CTRL);

    udelay(100);

    /* por noreset */
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_PHY_REQ;
    WRITE_UINT32(reg, USB2_CTRL);

    reg_1 = GET_UINT32(USB2_PHY_BASE_REG + 0x0014);
    reg_1 &= ~RG_PLL_EN_MASK;
    reg_1 |= RG_PLL_EN_VAL;
    WRITE_UINT32(reg_1, USB2_PHY_BASE_REG + 0x0014);

    udelay(2000);

    /* cancel TPOR */
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_PHY_PORT_TREQ;
    WRITE_UINT32(reg, USB2_CTRL);
    udelay(200);

    /* vcc reset */
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_VCC_SRST_REQ;
    WRITE_UINT32(reg, USB2_CTRL);
}

static void usb_ctrl_c(void)
{
    unsigned int reg;

    reg = GET_UINT32(REG_GUSB3PIPECTL0);
    reg |= PCS_SSP_SOFT_RESET;
    WRITE_UINT32(reg, REG_GUSB3PIPECTL0);
    udelay(20);

    reg = GET_UINT32(REG_GCTL);
    reg &= ~PORT_CAP_DIR;
    reg |= PORT_SET_HOST; /*[13:12] 01: Host; 10: Device; 11: OTG*/
    WRITE_UINT32(reg, REG_GCTL);
    udelay(20);

    reg = GET_UINT32(REG_GUSB3PIPECTL0);
    reg &= ~PCS_SSP_SOFT_RESET;
    reg &= ~PORT_DISABLE_SUSPEND;       //disable suspend
    WRITE_UINT32(reg, REG_GUSB3PIPECTL0);
    udelay(20);

    WRITE_UINT32(USB2_G_TXTHRCFG, GTXTHRCFG);
    WRITE_UINT32(USB2_G_RXTHRCFG, GRXTHRCFG);
    udelay(20);
}

void hisi_usb_phy_on(void)
{
    usb_vbus_config();
    usb_crg_c();
    usb_ctrl_c();

    /* USB2 eye config */
    usb_eye_config();

    /* USB2 trim config */
    usb_trim_config();

    /* USB2 svb config */
    usb_svb_config();
}

void hisi_usb_phy_off(void)
{
    unsigned int reg;

    /* por noreset */
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_PHY_REQ;
    WRITE_UINT32(reg, USB2_CTRL);
    udelay(2000);
    /* cancel TPOR */
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_PHY_PORT_TREQ;
    WRITE_UINT32(reg, USB2_CTRL);
    udelay(200);
    /* vcc reset */
    reg = GET_UINT32(USB2_CTRL);
    reg &= ~USB2_VCC_SRST_REQ;
    WRITE_UINT32(reg, USB2_CTRL);
}

void hiusb3_start_hcd(void)
{
    hisi_usb_phy_on();
}

void hiusb3_stop_hcd(void)
{
    hisi_usb_phy_off();
}

void hiusb3_host2device(void)
{
    unsigned int reg;

    reg = GET_UINT32(REG_GCTL);
    reg &= ~(0x3<<12);
    reg |= (0x1<<13); /*[13:12] 01: Host; 10: Device; 11: OTG*/
    WRITE_UINT32(reg, REG_GCTL);
    udelay(20);
}

int hiusb_is_device_mode(void)
{
    return (otg_usbdev_stat == 1);
}

void usb_otg_sw_set_device_state(void)
{
    otg_usbdev_stat = 1;
}

void usb_otg_sw_clear_device_state(void)
{
    otg_usbdev_stat = 0;
}

